using System;
using System.Collections;
using System.Drawing;
using System.Web;
using System.Web.UI;
using System.Data;
using System.Web.UI.WebControls;
using Deepcode.Flux.Core.Services.FluxClient;
using Deepcode.Flux.Core.Data;

namespace Deepcode.Flux.Core.UI.Controls
{
	/// <summary>
	/// StandardDataGrid implements the same functionality as asp:datagrid, but provides
	/// easy to use javascript binding commands to add javascript or additional
	/// attributes to child controls on the datarow. It is also designed to co-operate
	/// with DataPager controls.
	/// 
	/// In addition, the standard data grid provides better support for optional row selection
	/// (with ClickRowSelect = true), and for sorting using the column headers.
	/// </summary>
	public class StandardDataGrid : Deepcode.Flux.Core.UI.Controls.Base.BaseDataGrid, System.Web.UI.IPostBackEventHandler
	{
		#region New Component Properties
		/// <summary>
		/// Specify the SortExpression from the column which will be sorted by default.
		/// </summary>
		public string StartSort
		{
			get
			{
				return (string) ViewState["startsort"];
			}
			set
			{
				ViewState["startsort"] = value;
			}
		}
		/// <summary>
		/// property holding information about the current sort order. This is maintained by the grid
		/// under normal circumstances, however, if you change the sort order without using the
		/// standard header's being clicked, you will need to set this and currentsortdirection
		/// to ensure the header reflects the correct sort order. Set this field to the sortexpression
		/// of the column that is sortable.
		/// </summary>
		public string CurrentSort
		{
			get
			{
				string s = (string) ViewState["cs"];
				if( s == null ) s = StartSort;
				return s;
			}
			set
			{
				ViewState["cs"] = value;
			}
		}
		/// <summary>
		/// Private property holding information about whether the current sort
		/// is ascending or descending. Defaults to ascending. This is maintained by the grid
		/// under normal circumstances, however, if you change the sort order without using the
		/// standard header's being clicked, you will need to set this and currentsort
		/// to ensure the header reflects the correct sort order. 
		/// </summary>
		public DataSortDirection CurrentSortDirection
		{
			get
			{
				string s = (string) ViewState["csd"];
				if( s == null ) return DataSortDirection.ASCENDING;
				try
				{
					DataSortDirection d = (DataSortDirection) Enum.Parse( typeof( DataSortDirection ), s, true );
					return d;
				}
				catch
				{
					return DataSortDirection.ASCENDING;
				}
			}
			set
			{
				ViewState["csd"] = value.ToString();
			}
		}
		/// <summary>
		/// Set this property to true to allow whole rows in the datagrid to be selected
		/// </summary>
		public bool ClickRowSelect
		{
			get
			{
				object o = ViewState["ClickRowSelect"];
				if( o == null ) return true;
				return (bool) o;
			}
			set
			{
				ViewState["ClickRowSelect"] = value;
			}
		}
		/// <summary>
		/// Set this property to true to allow whole rows in the datagrid to be de-selected
		/// (must be used in conjunction with ClickRowSelect)
		/// </summary>
		public bool ClickRowDeselect
		{
			get
			{
				object o = ViewState["ClickRowDeSelect"];
				if( o == null ) return false;
				return (bool) o;
			}
			set
			{
				ViewState["ClickRowDeSelect"] = value;
			}
		}
		/// <summary>
		/// Set to true or false depending on whether or not you want the rows to highlight
		/// as the mouse moves over them.
		/// </summary>
		public bool MouseOverRows
		{
			get
			{
				object o = ViewState["MOR"];
				if( o == null ) return true;
				return (bool) o;
			}
			set
			{
				ViewState["MOR"] = value;
			}
		}
		#endregion
		#region Constructor - sets up the standard style properties
		/// <summary>
		/// Constructor - sets the default properties and look and feel for a 
		/// Flux styled datagrid.
		/// </summary>
		public StandardDataGrid()
		{
			this.ItemStyle.BackColor = StandardColours.ItemBack;
			this.ItemStyle.ForeColor = StandardColours.ItemFore;
			this.ItemStyle.VerticalAlign = VerticalAlign.Top;
			this.AlternatingItemStyle.BackColor = StandardColours.AltItemBack;
			this.AlternatingItemStyle.ForeColor = StandardColours.AltItemFore;
			this.AlternatingItemStyle.VerticalAlign = VerticalAlign.Top;
			this.SelectedItemStyle.BackColor = StandardColours.SelItemBack;
			this.SelectedItemStyle.ForeColor = StandardColours.SelItemFore;
			this.SelectedItemStyle.VerticalAlign = VerticalAlign.Top;
			
			this.HeaderStyle.BackColor = StandardColours.HeaderBack;
			this.HeaderStyle.ForeColor = StandardColours.HeaderFore;
			this.HeaderStyle.Font.Bold = true;
			this.HeaderStyle.Height = new Unit("30px");

			this.PagerStyle.Visible = false;

			this.BackColor = Color.White;
			this.BorderColor = Color.Silver;
			this.BorderStyle = BorderStyle.None;
			this.CellPadding = 8;
			this.CellSpacing=0;
			this.GridLines = GridLines.Horizontal;
			
			this.Width = new Unit("100%");
			this.PageSize = 10;
			this.AllowPaging = true;
			this.AutoGenerateColumns = false;
		}
		#endregion
		#region Javascript Control Binding
		/// <summary>
		/// Holds information for a control->javascript binding in the grid
		/// </summary>
		private class JSBinding
		{
			public string ControlID;
			public string EventAttribute;
			public string Javascript;
		}
		/// <summary>
		/// Holds the array of Javascript bindings in the grid
		/// </summary>
		private ArrayList jsBindings = new ArrayList();
		/// <summary>
		/// Bind an attribute onto a control at runtime. For example, this 
		/// function is generally used to bind javascript to a control.
		/// <code>
		///		BindJavascript("btnDelete", "onclick", "return ConfirmDelete();");
		/// </code>
		/// </summary>
		/// <param name="ControlID"></param>
		/// <param name="Javascript"></param>
		public void BindJavascript( string ControlID, string EventName, string Javascript )
		{
			JSBinding jsb = new JSBinding();
			jsb.ControlID = ControlID;
			jsb.EventAttribute = EventName;
			jsb.Javascript = Javascript;
			jsBindings.Add( jsb );
		}
		/// <summary>
		/// The Grid's ItemDataBound handler specifically for binding up javascript
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void BindJavascripts(object sender, DataGridItemEventArgs e)
		{
			if( e.Item.ItemType != ListItemType.AlternatingItem &&
				e.Item.ItemType != ListItemType.EditItem &&
				e.Item.ItemType != ListItemType.Item &&
				e.Item.ItemType != ListItemType.SelectedItem ) return;

			foreach( JSBinding jsb in jsBindings )
			{
				System.Web.UI.Control linkitem = e.Item.FindControl(jsb.ControlID);
				if( linkitem is System.Web.UI.WebControls.WebControl )
				{
					WebControl wcLinkItem = (WebControl) linkitem;
					wcLinkItem.Attributes.Add(jsb.EventAttribute, jsb.Javascript);
				}
			}
		}
		#endregion
		#region OnInit
		protected override void OnInit(EventArgs e)
		{
			this.LoadClientScript( "StandardDataGrid/StandardDataGrid.js", "Deepcode.Flux.Core.UI.Controls.StandardDataGrid" );
			// Bind javascripts to items
			this.ItemDataBound += new DataGridItemEventHandler(BindJavascripts);
			// Bind row click selection to items
			this.ItemDataBound += new DataGridItemEventHandler(BindRowClickAttributes);
			// Bind special header creations to the items
			this.ItemCreated += new DataGridItemEventHandler(StandardDataGrid_ItemCreated);
			// Replace the sort command with our own
			this.SortCommand += new DataGridSortCommandEventHandler(DataGridSort);
			base.OnInit (e);
		}
		#endregion
		#region Row Click Attributes
		/// <summary>
		/// Bind the row click postback command to the rows if appropriate
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void BindRowClickAttributes(object sender, DataGridItemEventArgs e)
		{
			if( ! this.ClickRowSelect ) return;
			if( e.Item.ItemType != ListItemType.AlternatingItem &&
				e.Item.ItemType != ListItemType.EditItem &&
				e.Item.ItemType != ListItemType.Item &&
				e.Item.ItemType != ListItemType.SelectedItem ) return;

			e.Item.Attributes.Add( "onclick",
				Page.ClientScript.GetPostBackEventReference( this, "SelectRow|" + e.Item.ItemIndex ));
		}
		/// <summary>
		/// Handle the select row command.
		/// </summary>
		/// <param name="eventArgument"></param>
		public void RaisePostBackEvent(string eventArgument)
		{
			if( eventArgument.StartsWith("SelectRow|" ))
			{
				int RowIndex = -1;
				try
				{
					string [] atts = eventArgument.Split('|');
					RowIndex = Int32.Parse( atts[1] );
				}
				catch{ RowIndex = -1; }

				if (this.SelectedIndex != RowIndex)
				{
					this.SelectedIndex = RowIndex;
					this.OnSelectedIndexChanged(System.EventArgs.Empty);
				}
				else
				{
					// Clicked to select same one - de-select it if we have that behaviour
					if (this.ClickRowDeselect)
					{
						this.SelectedIndex = -1;
						this.OnSelectedIndexChanged(System.EventArgs.Empty);
					}
				}
			}
		}
		#endregion
		#region New Sorting functionality
		/// <summary>
		/// Delegate to describe the sort direction event
		/// </summary>
		public delegate void OnSortDirectionCommand( object source, string SortExpression, DataSortDirection Direction );
		/// <summary>
		/// The event that is fired when the user changes the sort order or sort direction
		/// </summary>
		public event OnSortDirectionCommand SortDirectionCommand;
		/// <summary>
		/// Handler for the current sort functionality
		/// </summary>
		/// <param name="source"></param>
		/// <param name="e"></param>
		private void DataGridSort(object source, DataGridSortCommandEventArgs e)
		{
			// If it's the same sort expression, switch the sort direction
			if( e.SortExpression == CurrentSort )
			{
				CurrentSortDirection = (CurrentSortDirection == DataSortDirection.ASCENDING) ? DataSortDirection.DESCENDING: DataSortDirection.ASCENDING;
				// Trigger the sort event
				if( SortDirectionCommand != null )
					SortDirectionCommand( this, CurrentSort, CurrentSortDirection );
			}
			else
			{
				// Change sort expression to the new expression requested
				CurrentSort = e.SortExpression;
				// Trigger the sort event
				if( SortDirectionCommand != null )
					SortDirectionCommand( this, CurrentSort, CurrentSortDirection );
			}
				
		}
		/// <summary>
		/// Special handler for item created. Intercepts the creation of header
		/// cells to insert the graphic representing the current sort direction.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void StandardDataGrid_ItemCreated(object sender, DataGridItemEventArgs e)
		{
			// Was the item created a header item ?
			if( e.Item.ItemType == ListItemType.Header )
			{
				// get all the table cells 
				foreach( TableCell cell in e.Item.Cells )
				{
					// Ensure it has controls
					if( cell.Controls.Count > 0 )
						// Ensure it's a link button (that is how SORT command is implemented)
						if( cell.Controls[0] is LinkButton )
						{
							// Get the link button
							LinkButton n = (LinkButton) cell.Controls[0];
							// Ensure it is actually a sort command
							if( n.CommandName.ToUpper() == "SORT" )
							{
								// See if it's the current sort expression - if it is, this needs the sort moniker.
								if( n.CommandArgument == this.CurrentSort )
								{
									// This is the one - add an image into the text
									n.Text += "<img src='" 
										+ ClientHelper.GetFluxClientFile("StandardDataGrid/Img/" + this.CurrentSortDirection.ToString() + ".gif")
										+ "' width='18' height='8' border='0' align='middle'/>";
									break;
								}
							}
						}
				}
			}
			else
			{
				if( MouseOverRows )
				{
					if( e.Item.ItemType == ListItemType.Item ||
						e.Item.ItemType == ListItemType.AlternatingItem)
					{
						e.Item.Attributes.Add("onmouseover", "StandardDataGrid_RowOver(this);");
						e.Item.Attributes.Add("onmouseout", "StandardDataGrid_RowOut(this);");
					}
				}
			}
		}
		#endregion
	}
}
